package com.stark.commons.lang.util;

import com.stark.commons.lang.enums.MediaType;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;

/**
 * 图片处理工具类。
 * @author Ben
 * @since 1.0.0
 * @version 1.0.0
 */
@SuppressWarnings("unused")
public class ImageUtil {
	
	/**
	 * 获取图片宽度。
	 * @param bytes 图片字节码。
	 * @return 宽度像素值。
	 */
	public static int getWidth(byte[] bytes) throws IOException {
		return ImageIO.read(new ByteArrayInputStream(bytes)).getWidth();
	}
	
	/**
	 * 获取图片高度。
	 * @param bytes 图片字节码。
	 * @return 高度像素值。
	 */
	public static int getHeight(byte[] bytes) throws IOException {
		return ImageIO.read(new ByteArrayInputStream(bytes)).getHeight();
	}
	
	/**
	 * 获取图片格式（后缀）。
	 * @param bytes 图片字节码。
	 * @return 图片格式（后缀）。
	 */
	public static String getFormat(byte[] bytes) throws IOException {
		try (ByteArrayInputStream srcInputStream = new ByteArrayInputStream(bytes); ImageInputStream imageInputStream = ImageIO.createImageInputStream(srcInputStream)) {
			Iterator<ImageReader> imageReaders = ImageIO.getImageReaders(imageInputStream);
			while (imageReaders.hasNext()) {
				String format = imageReaders.next().getFormatName();
				if (StringUtils.isNotBlank(format)) {
					return format.toLowerCase();
				}
			}
		}
		return null;
	}
	
	/**
	 * 获取图片格式（后缀）。
	 * @param bytes 图片字节码。
	 * @param filename 文件名。
	 * @return 图片格式（后缀）。
	 */
	public static String getFormat(byte[] bytes, String filename) throws IOException {
		String format = getFormat(bytes);
		if (StringUtils.isBlank(format)
				&& StringUtils.isNotBlank(filename) && filename.contains(".")) {
			format = StringUtils.substringAfterLast(filename, ".").toLowerCase();
		}
		return format;
	}
	
	/**
	 * 获取图片媒体类型。
	 * @param bytes 图片字节码。
	 * @return 图片媒体类型。
	 */
	public static MediaType getMediaType(byte[] bytes) throws IOException {
		return MediaType.getByExtension(getFormat(bytes));
	}
	
	/**
	 * 获取图片媒体类型。
	 * @param bytes 图片字节码。
	 * @param filename 文件名。
	 * @return 图片媒体类型。
	 */
	public static MediaType getMediaType(byte[] bytes, String filename) throws IOException {
		return MediaType.getByExtension(getFormat(bytes, filename));
	}
	
	/**
	 * 组织 base64 编码的图片字符串。
	 * <p>将该方法所得的字符串置成 &ltimg> 标签的 src ， 即可显示图片。
	 * @param bytes 图片字节码。
	 * @return base64 编码的图片字符串。
	 * @see org.apache.commons.codec.binary.Base64#encodeBase64String(byte[])
	 */
	public static String getDataUrl(byte[] bytes) throws IOException {
		return getDataUrl(bytes, null);
	}
	
	/**
	 * 组织 base64 编码的图片字符串。
	 * <p>将该方法所得的字符串置成 &ltimg> 标签的 src ， 即可显示图片。
	 * @param bytes 图片字节码。
	 * @param filename 文件名。
	 * @return base64 编码的图片字符串。
	 * @see org.apache.commons.codec.binary.Base64#encodeBase64String(byte[])
	 */
	public static String getDataUrl(byte[] bytes, String filename) throws IOException {
		if (ArrayUtils.isEmpty(bytes)) {
			return "";
		}
		MediaType mediaType = getMediaType(bytes, filename);
		if (mediaType == null) {
			return "";
		}
		return "data:" + mediaType.getContentType() + ";base64," + Base64.encodeBase64String(bytes);
	}
	
	/**
	 * 解析 base64 编码的图片字符串获得字节码。
	 * @param dataUrl base64 编码的图片字符串。
	 * @return 图片字节码。
	 * @see org.apache.commons.codec.binary.Base64#decodeBase64(String)
	 */
	public static byte[] getBytesFromDataUrl(String dataUrl) {
		dataUrl = StringUtils.substringAfter(dataUrl, "base64,");
		return Base64.decodeBase64(dataUrl);
	}
	
	/**
	 * 自动缩放。
	 * @param bytes 图片字节码。
	 * @param maxSize 最大体积，单位字节，小于等于 0 表示不限制。
	 * @return 缩放后的图片。
	 */
	public static byte[] autoSize(byte[] bytes, long maxSize) throws IOException {
		double quality = maxSize <= 0
				? 1.0
				: 1.0 * maxSize / bytes.length;
		if (quality >= 1) {
			return bytes;
		}

		try (InputStream in = new ByteArrayInputStream(bytes); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
			Thumbnails.of(in)
					.scale(1f)
					.outputQuality(quality)
					.toOutputStream(out);
			bytes = out.toByteArray();
		}
		return bytes;
	}
	
}
